/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>

#include <functional>
#include <memory>
#include <string>
#include <vector>

#include <mc/common.h>
#include <test_utilities/test_base.h>
#include <mc/crypto/crc32.h>
#include <mc/memory.h>
#include <ncsi/ncsi_huawei.h>
#include <ncsi/ncsi_protocol.h>
#include <ncsi/ncsi_socket.h>
#include <ncsi_over_mctp/ncsi_over_mctp_huawei.h>
#include <smbus/smbus.h>

#include "../../../../../drivers/pcie_nic_card/hisi/interface/network_port.h"
#include "../../../../../drivers/pcie_nic_card/hisi/interface/network_port/link_info.h"
#include "../../../../../drivers/pcie_nic_card/hisi/interface/network_port/metrics.h"
#include "../../../../../drivers/pcie_nic_card/hisi/interface/network_port/data_center_bridging.h"
#include "../../../../../drivers/pcie_nic_card/hisi/interface/network_port/lldp_receive.h"
#include <map>

// 声明外部函数
namespace dev {
void set_mctp_request_handler(std::function<bool(const mc::dict&, const std::vector<uint8_t>&,
                                                  std::string&)> handler);
}

namespace {

constexpr uint16_t host_to_be16(uint16_t value) {
    return static_cast<uint16_t>(((value & 0x00FFU) << 8U) | ((value & 0xFF00U) >> 8U));
}

// Mock smbus - 支持根据 opcode 返回不同的响应
class mock_smbus : public dev::smbus {
public:
    mock_smbus() : dev::smbus(nullptr) {
    }

    void set_send_request_result(const std::string& data_result) {
        m_data_result = data_result;
    }

    void set_send_request_result_for_opcode(uint16_t opcode, const std::string& data_result) {
        m_opcode_results[opcode] = data_result;
    }

    std::pair<bool, std::vector<uint8_t>> send_and_receive(const std::string& data,
                                                           uint32_t len) override {
        // 从请求数据中解析 opcode
        // dev::smbus 请求格式：reserved(2) + opcode(2) + offset(4) + length(4)
        uint16_t request_opcode = 0x04;  // 默认值
        if (data.length() >= 4) {
            // opcode 在 reserved (2 bytes) 之后
            request_opcode = static_cast<uint8_t>(data[2]) | (static_cast<uint8_t>(data[3]) << 8);
        }

        // 根据 opcode 获取对应的响应数据
        std::string response_data = m_data_result;
        if (m_opcode_results.find(request_opcode) != m_opcode_results.end()) {
            response_data = m_opcode_results[request_opcode];
        }

        // 如果数据为空，返回空响应
        if (response_data.empty()) {
            return std::make_pair(false, std::vector<uint8_t>());
        }

        uint32_t data_len = static_cast<uint32_t>(response_data.length());

        std::vector<uint8_t> response;
        response.reserve(12 + data_len + 4);

        response.push_back(0x00);
        response.push_back(0x00);

        // opcode (2 bytes) - 从请求中获取
        response.push_back(static_cast<uint8_t>(request_opcode & 0xFF));
        response.push_back(static_cast<uint8_t>((request_opcode >> 8) & 0xFF));

        response.push_back(static_cast<uint8_t>(data_len & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 24) & 0xFF));

        response.push_back(static_cast<uint8_t>(data_len & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((data_len >> 24) & 0xFF));

        response.insert(response.end(), response_data.begin(), response_data.end());

        std::string bin_data(reinterpret_cast<const char*>(response.data()), response.size());
        uint32_t crc32 = mc::crypto::CRC32().calculate(bin_data, 0, true);
        response.push_back(static_cast<uint8_t>(crc32 & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 8) & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 16) & 0xFF));
        response.push_back(static_cast<uint8_t>((crc32 >> 24) & 0xFF));

        return std::make_pair(true, response);
    }

private:
    std::string m_data_result;
    std::map<uint16_t, std::string> m_opcode_results;
};

// Test service
class test_service : public mc::engine::service {
public:
    test_service(const std::string& name) : mc::engine::service(name) {
    }
};

// Test fixture
class network_port_test : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service.init();
        m_test_service.start();
        m_mctp = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE_NCSI, "");
    }

    static void TearDownTestSuite() {
        m_test_service.stop();
        m_mctp.reset();
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {
        m_port = mc::make_shared<dev::NetworkPort>();
        m_port->PortId.set_value(static_cast<uint8_t>(1));
    }

    void TearDown() override {
        // 等待所有异步操作完成（如定时器回调），避免在服务停止时回调仍在执行
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        // 清理实例成员
        m_port.reset();
        dev::set_mctp_request_handler(nullptr);
    }

    mc::shared_ptr<dev::NetworkPort> m_port;
    static test_service m_test_service;
    static mc::shared_ptr<dev::mctp> m_mctp;
};

// 静态成员初始化
test_service network_port_test::m_test_service{"bmc.kepler.test_nic"};
mc::shared_ptr<dev::mctp> network_port_test::m_mctp;

// ========== NetworkPort_Metrics 测试 ==========

// Test fixture for NetworkPort_Metrics
class network_port_metrics_test : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service.init();
        m_test_service.start();
        m_mctp = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE_NCSI, "");
        m_metrics = mc::make_shared<dev::NetworkPort_Metrics>();
    }

    static void TearDownTestSuite() {
        m_test_service.stop();
        m_metrics.reset();
        m_mctp.reset();
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {
    }

    void TearDown() override {
        // 等待所有异步操作完成（如定时器回调），避免在服务停止时回调仍在执行
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        dev::set_mctp_request_handler(nullptr);
    }

    static test_service m_test_service;
    static mc::shared_ptr<dev::mctp> m_mctp;
    static mc::shared_ptr<dev::NetworkPort_Metrics> m_metrics;
};

// 静态成员初始化
test_service network_port_metrics_test::m_test_service{"bmc.kepler.test_metrics"};
mc::shared_ptr<dev::mctp> network_port_metrics_test::m_mctp;
mc::shared_ptr<dev::NetworkPort_Metrics> network_port_metrics_test::m_metrics;

// 辅助函数：创建get_port_metrics响应
std::string make_get_port_metrics_response() {
    controller_packet_statistics_rsp response{};
    response.rsp_code   = host_to_be16(COMMAND_COMPLETED);
    response.reason_code = host_to_be16(NOERROR_OR_NOREASON);
    response.counters_cleared_from_last_read_ms_bits = 0;
    response.counters_cleared_from_last_read_ls_bists = 0;
    response.total_bytes_received                = mc::hton(static_cast<uint64_t>(1000));
    response.total_bytes_transmitted              = mc::hton(static_cast<uint64_t>(2000));
    response.total_unicast_packets_received      = mc::hton(static_cast<uint64_t>(100));
    response.total_multicast_packets_received    = mc::hton(static_cast<uint64_t>(200));
    response.total_broadcast_packets_received     = mc::hton(static_cast<uint64_t>(300));
    response.total_unicast_packets_transmitted   = mc::hton(static_cast<uint64_t>(400));
    response.total_multicast_packets_transmitted = mc::hton(static_cast<uint64_t>(500));
    response.total_broadcast_packets_transmitted = mc::hton(static_cast<uint64_t>(600));
    response.fcs_received_errors                 = mc::hton(static_cast<uint32_t>(10));
    response.runt_packets_received               = mc::hton(static_cast<uint32_t>(20));
    response.jabber_packets_received             = mc::hton(static_cast<uint32_t>(30));
    response.single_collision_transmit_frames     = mc::hton(static_cast<uint32_t>(40));
    response.multiple_collision_transmit_frames  = mc::hton(static_cast<uint32_t>(50));
    response.late_collision_frames               = mc::hton(static_cast<uint32_t>(60));
    response.excessive_collision_frames          = mc::hton(static_cast<uint32_t>(70));
    response.alignment_errors                    = mc::hton(static_cast<uint32_t>(80));
    response.check_sum                           = 0;
    response.FCS                                 = 0;
    return std::string(reinterpret_cast<const char*>(&response),
                       GET_CONTROLLER_PACKET_STATISTICS_RSP_LEN);
}

TEST_F(network_port_metrics_test, update_port_metrics_by_ncsi_success) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string& rsp) {
        rsp = make_get_port_metrics_response();
        return true;
    });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_metrics->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(1000), 1);

    bool result = m_metrics->update_port_metrics_by_ncsi(1);
    EXPECT_TRUE(result);
    EXPECT_EQ(m_metrics->RXFrames.get_value().as<uint64_t>(), 1000);
    EXPECT_EQ(m_metrics->TXFrames.get_value().as<uint64_t>(), 2000);
    EXPECT_EQ(m_metrics->RXUnicastFrames.get_value().as<uint64_t>(), 100);
    EXPECT_EQ(m_metrics->TXUnicastFrames.get_value().as<uint64_t>(), 400);
    EXPECT_EQ(m_metrics->RXMulticastFrames.get_value().as<uint64_t>(), 200);
    EXPECT_EQ(m_metrics->TXMulticastFrames.get_value().as<uint64_t>(), 500);
    EXPECT_EQ(m_metrics->RXBroadcastFrames.get_value().as<uint64_t>(), 300);
    EXPECT_EQ(m_metrics->TXBroadcastFrames.get_value().as<uint64_t>(), 600);
    EXPECT_EQ(m_metrics->RXFCSErrors.get_value().as<uint64_t>(), 10);
    EXPECT_EQ(m_metrics->RXUndersizeFrames.get_value().as<uint64_t>(), 20);
    EXPECT_EQ(m_metrics->RXOversizeFrames.get_value().as<uint64_t>(), 30);
    EXPECT_EQ(m_metrics->TXSingleCollisions.get_value().as<uint64_t>(), 40);
    EXPECT_EQ(m_metrics->TXMultipleCollisions.get_value().as<uint64_t>(), 50);
    EXPECT_EQ(m_metrics->TXLateCollisions.get_value().as<uint64_t>(), 60);
    EXPECT_EQ(m_metrics->TXExcessiveCollisions.get_value().as<uint64_t>(), 70);
    EXPECT_EQ(m_metrics->RXFrameAlignmentErrors.get_value().as<uint64_t>(), 80);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_metrics->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_metrics_test, update_port_metrics_by_ncsi_failure) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string&) { return false; });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_metrics->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(1000), 1);

    bool result = m_metrics->update_port_metrics_by_ncsi(1);
    EXPECT_FALSE(result);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_metrics->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_metrics_test, start_ncsi_update_task_huawei_sets_ncsi_object) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string& rsp) {
        rsp = make_get_port_metrics_response();
        return true;
    });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_metrics->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(100), 1);

    bool result = m_metrics->update_port_metrics_by_ncsi(1);
    EXPECT_TRUE(result);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_metrics->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_metrics_test, stop_ncsi_update_task_cleans_up_resources) {
    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_metrics->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(100), 1);
    m_metrics->stop_ncsi_update_task();
    m_metrics->stop_ncsi_update_task();  // 再次调用应该不会崩溃
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

// ========== NetworkPort_DataCenterBridging 测试 ==========

// Test fixture for NetworkPort_DataCenterBridging
class network_port_data_center_bridging_test : public mc::test::TestWithEngine {
protected:
    static void SetUpTestSuite() {
        TestWithEngine::SetUpTestSuite();
        m_test_service.init();
        m_test_service.start();
        m_mctp = mc::make_shared<dev::mctp>(nullptr, 0, dev::MCTP_MESSAGE_TYPE_NCSI, "");
    }

    static void TearDownTestSuite() {
        m_test_service.stop();
        m_mctp.reset();
        TestWithEngine::TearDownTestSuite();
    }

    void SetUp() override {
        m_dcbx = mc::make_shared<dev::NetworkPort_DataCenterBridging>();
    }

    void TearDown() override {
        m_dcbx.reset();
        m_mctp.reset();
    }

    mc::shared_ptr<dev::NetworkPort_DataCenterBridging> m_dcbx;
    static test_service m_test_service;
    static mc::shared_ptr<dev::mctp> m_mctp;
};

// 静态成员初始化
test_service network_port_data_center_bridging_test::m_test_service{"bmc.kepler.test_dcbx"};
mc::shared_ptr<dev::mctp> network_port_data_center_bridging_test::m_mctp;

// 辅助函数：创建get_dcbx_status响应
std::string make_get_dcbx_status_response() {
    oem_huawei_dcbx_status_rsp_t response{};
    response.public_rsp_header.response_code   = host_to_be16(0);
    response.public_rsp_header.reason_code     = host_to_be16(0);
    response.public_rsp_header.manufacturer_id = HUAWEI_MANUFACTURE_ID;
    response.public_rsp_header.sub_command_id  = OEM_HUAWEI_GET_DCBX_STATUS;
    // 初始化数组
    for (int i = 0; i < MCTP_NCSI_DCBX_FIELD_LEN; i++) {
        response.cos2_up[i] = static_cast<uint8_t>(i);
        response.up_pgid[i] = static_cast<uint8_t>(i + 10);
        response.pgpct[i]   = static_cast<uint8_t>(i + 20);
        response.strict[i]  = static_cast<uint8_t>(i + 30);
    }
    response.pfcmap = 0x55;
    response.check_sum = 0;
    return std::string(reinterpret_cast<const char*>(&response),
                       OEM_HUAWEI_DCBX_STATUS_RSP_LEN);
}

TEST_F(network_port_data_center_bridging_test, update_dcbx_status_by_ncsi_success) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string& rsp) {
        rsp = make_get_dcbx_status_response();
        return true;
    });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_dcbx->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(1000), 1);

    bool result = m_dcbx->update_dcbx_status_by_ncsi(1);
    EXPECT_TRUE(result);

    auto up2cos = m_dcbx->Up2cos.get_value().as<std::vector<uint8_t>>();
    auto uppgid = m_dcbx->Uppgid.get_value().as<std::vector<uint8_t>>();
    auto pgpct  = m_dcbx->Pgpct.get_value().as<std::vector<uint8_t>>();
    auto strict = m_dcbx->PgStrict.get_value().as<std::vector<uint8_t>>();

    EXPECT_EQ(up2cos.size(), MCTP_NCSI_DCBX_FIELD_LEN);
    EXPECT_EQ(uppgid.size(), MCTP_NCSI_DCBX_FIELD_LEN);
    EXPECT_EQ(pgpct.size(), MCTP_NCSI_DCBX_FIELD_LEN);
    EXPECT_EQ(strict.size(), MCTP_NCSI_DCBX_FIELD_LEN);

    for (int i = 0; i < MCTP_NCSI_DCBX_FIELD_LEN; i++) {
        EXPECT_EQ(up2cos[i], static_cast<uint8_t>(i));
        EXPECT_EQ(uppgid[i], static_cast<uint8_t>(i + 10));
        EXPECT_EQ(pgpct[i], static_cast<uint8_t>(i + 20));
        EXPECT_EQ(strict[i], static_cast<uint8_t>(i + 30));
    }

    EXPECT_EQ(m_dcbx->Pfcmap.get_value().as<uint8_t>(), 0x55);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_dcbx->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_data_center_bridging_test, update_dcbx_status_by_ncsi_failure) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string&) { return false; });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_dcbx->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(1000), 1);

    bool result = m_dcbx->update_dcbx_status_by_ncsi(1);
    EXPECT_FALSE(result);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_dcbx->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_data_center_bridging_test, start_ncsi_update_task_huawei_sets_ncsi_object) {
    dev::set_mctp_request_handler([](const mc::dict&, const std::vector<uint8_t>&,
                                     std::string& rsp) {
        rsp = make_get_dcbx_status_response();
        return true;
    });

    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_dcbx->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(100), 1);

    bool result = m_dcbx->update_dcbx_status_by_ncsi(1);
    EXPECT_TRUE(result);
    
    // 显式停止定时器，避免在 TearDown 时定时器仍在运行
    m_dcbx->stop_ncsi_update_task();
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(network_port_data_center_bridging_test, stop_ncsi_update_task_cleans_up_resources) {
    auto ncsi = std::make_shared<dev::ncsi_over_mctp_huawei>(*m_mctp);
    m_dcbx->start_ncsi_update_task_huawei(ncsi, mc::milliseconds(100), 1);
    m_dcbx->stop_ncsi_update_task();
    m_dcbx->stop_ncsi_update_task();  // 再次调用应该不会崩溃
    // 等待所有异步操作完成（如定时器回调），避免在 TearDown 时回调仍在执行
    // 增加等待时间，确保定时器回调完全停止
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

// ========== NetworkPort_LLDPReceive 测试 ==========
// NetworkPort_LLDPReceive 类只有默认构造函数和析构函数，没有其他方法，不需要测试

} // namespace

